﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

using Matchingo.Common.Constants;
using Matchingo.Common.Types.Types;
using Matchingo.Common.Types.Enumerations;
using Matchingo.Common.Types.EventArguments;
using Matchingo.Common.Types.Events;
using Matchingo.Common.Types.Interfaces;

namespace Matchingo.Modules.CardGridModule
{
    public class CardGridViewModel : IModule
    {
        #region ========== Private Properties ==========

        private readonly IUnityContainer Container;
        private readonly IRegionManager RegionManager;
        private readonly IEventAggregator EventAggregator;
        private readonly IMemoryGameLogic GameLogic;
        private readonly ICardSetProvider CardProvider;
        private readonly IGameInputFacade GameInputFacade;
        private readonly ICardUserControlFactory CardUserControlFactory;

        private ICardGridView View { get; set; }

        #endregion

        #region ========== Public Properties ==========

        /// <summary>
        /// The score to display next to the player's name
        /// </summary>
        public int GridColumnCount { get; private set; }

        /// <summary>
        /// The score to display next to the player's name
        /// </summary>
        public int GridRowCount { get; private set; }

        public ObservableCollection<Card> GameCardSet { get; private set; }

        public List<Uri> CardImageUriList 
		{
			get { return CardProvider.CardImages; }
		}

        #endregion

        #region ========== Constructor ==========

        public CardGridViewModel(IUnityContainer container,
            IEventAggregator eventAggregator,
            IRegionManager regionManager, 
            IMemoryGameLogic gameLogic, 
            ICardSetProvider cardProvider,                               
            IGameInputFacade gameInputFacade, 
            ICardUserControlFactory cardUserControlFactory)
        {
            Container = container;
            EventAggregator = eventAggregator;
            RegionManager = regionManager;
            GameLogic = gameLogic;
            CardProvider = cardProvider;
            GameInputFacade = gameInputFacade;
            CardUserControlFactory = cardUserControlFactory;
        }

        #endregion

        #region ========== Public Methods ==========

        public void Initialize()
        {
            View = Container.Resolve<ICardGridView>();
            View.Initialize(this);

            // add the view to the RegionManager
            IRegion region = RegionManager.Regions[CommonConstants.SHELL_REGION_CardGrid];
            region.Add(View);

            UpdateLocalValuesFromGameLogic();

            SinkEvents();
        }

        public void FlipCard(Guid uniqueID)
        {
            GameInputFacade.FlipCard(uniqueID);
        }

        public UserControl GetCardControl(Card c, Image front)
        {
            return CardUserControlFactory.GetCardUserControl(c, front);
        }

        #endregion

        #region ========== Private Methods ==========

        private void SinkEvents()
        {
            GameLogic.PropertyChanged += GameLogicPropertyChanged;            
            GameLogic.CardsMatched += GameLogicCardsMatched;

            EventAggregator.GetEvent<GameStartedEvent>().Subscribe(GameStartedEventFired, ThreadOption.UIThread);
			EventAggregator.GetEvent<CardFlippedEvent>().Subscribe(CardFlippedEventFired, ThreadOption.UIThread);        
		}

        private void UpdateLocalValuesFromGameLogic()
        {
            if (GameLogic.CurrentGame != null)
            {
                GameCardSet = GameLogic.CurrentGame.CardsSet;
            }
            
            GridColumnCount = GameLogic.GridColumns;
            GridRowCount = GameLogic.GridRows;
        }

        void GameLogicCardsMatched(object sender, MatchEventArgs e)
        {
            Point p1 = View.GetCenterOfCardRelativeToParent(e.Card1);
            Point p2 = View.GetCenterOfCardRelativeToParent(e.Card2);

            int fireCount = GameLogic.MatchComboCount;

            if (fireCount == 0)
                throw new ArgumentException("CardGridModule.GameLogicCardsMatched() - MatchComboCount must be greater than 0");

			// we only want to play sounds if the current game is a real game (and not a replay) 
			bool playSounds = !GameLogic.CurrentGame.ArtificialGame || GameLogic.CurrentGame.ReplayGame;
			
            // delay to let card flip to complete
            DispatcherTimer dt = new DispatcherTimer();
            dt.Interval = new TimeSpan(0, 0, 0, 0, CommonConstants.TIME_BETWEEN_CARDS_MATCH_AND_REMOVE_FROM_GAME_GRID);
            dt.Tick += (s, e2) =>
            {
                // we dont want the sounds to double up, so we only playsounds (if we are) for the first particle fire
                FireParticlesEventArgs particleArgs1 = new FireParticlesEventArgs(p1.X, p1.Y, 
                    ParticleTypeEnumeration.ParticleType.MatchCelebration, GameLogic.MatchComboCount, playSounds);

                FireParticlesEventArgs particleArgs2 = new FireParticlesEventArgs(p2.X, p2.Y,
                    ParticleTypeEnumeration.ParticleType.MatchCelebration, GameLogic.MatchComboCount, false);

                EventAggregator.GetEvent<ParticleFireRequestEvent>().Publish(particleArgs1);
                EventAggregator.GetEvent<ParticleFireRequestEvent>().Publish(particleArgs2);

                // we only want the sounds to play on the first burst, so turn off the sounds after the first one
				playSounds = false;
                fireCount--;

                if (fireCount == 0)
                    dt.Stop();
            };
            dt.Start();
        }

        #endregion      

        #region ========== Event Handlers ==========

		// NOTE:: All methods called via the EventAggregator subscription must be public in Silverlight
        public void GameStartedEventFired(Game g)
        {
            View.GridDefinitionUpdated();
        }

		// NOTE:: All methods called via the EventAggregator subscription must be public in Silverlight
		public void CardFlippedEventFired(Guid uniqueID)
		{
			// we want to play the sounds for replays, but not for replaygames
			bool playSounds = !GameLogic.CurrentGame.ArtificialGame || GameLogic.CurrentGame.ReplayGame;
			Point p1 = View.GetCenterOfCardRelativeToParent(uniqueID);

		    FireParticlesEventArgs particleArgs = new FireParticlesEventArgs(p1.X, p1.Y, 
                ParticleTypeEnumeration.ParticleType.FlipCelebration,
				GameLogic.MatchComboCount, playSounds);

		    EventAggregator.GetEvent<ParticleFireRequestEvent>().Publish(particleArgs);
		}

        void GameLogicPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            UpdateLocalValuesFromGameLogic();
        }

        #endregion
    }
}
